home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / vim_src.zip / MSDOS.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  15KB  |  828 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMitation
  4.  *
  5.  * Code Contributions By:        Bram Moolenaar            mool@oce.nl
  6.  *                                Tim Thompson            twitch!tjt
  7.  *                                Tony Andrews            onecom!wldrdg!tony
  8.  *                                G. R. (Fred) Walter     watmath!watcgl!grwalter
  9.  */
  10.  
  11. /*
  12.  * msdos.c
  13.  *
  14.  * MSDOS system-dependent routines.
  15.  * A cheap plastic imitation of the amiga dependent code.
  16.  * A lot in this file was made by Juergen Weigert (jw).
  17.  */
  18.  
  19. #include "vim.h"
  20. #include "globals.h"
  21. #include "param.h"
  22. #include "proto.h"
  23. #include <conio.h>
  24. #include <fcntl.h>
  25. #include <bios.h>
  26.  
  27. static int WaitForChar __ARGS((int));
  28. static int cbrk_handler __ARGS(());
  29.  
  30. #ifdef WILD_CARDS
  31. typedef struct filelist
  32. {
  33.     char    **file;
  34.     int        nfiles;
  35.     int        maxfiles;
  36. } FileList;
  37.  
  38. static void        addfile __ARGS((FileList *, char *));
  39. static int        pstrcmp __ARGS((char **, char **));
  40. static int        no_wildcard __ARGS((char *));
  41. static void        strlowcpy __ARGS((char *, char *));
  42. static int        expandpath __ARGS((FileList *, char *, int, int, int));
  43. #endif
  44.  
  45. static int cbrk_pressed = FALSE;    /* set by ctrl-break interrupt */
  46. static int ctrlc_pressed = FALSE;    /* set when ctrl-C or ctrl-break detected */
  47. static int delayed_redraw = FALSE;    /* set when ctrl-C detected */
  48.  
  49. /*
  50.  * the number of calls to Write is reduced by using the buffer "outbuf"
  51.  */
  52. #define BSIZE    2048
  53. static u_char    outbuf[BSIZE];
  54. static int        bpos = 0;
  55.  
  56. #ifdef DEBUG
  57. /*
  58.  * Put two characters in the video buffer without calling BIOS or DOS.
  59.  */
  60. blink(n)
  61.     int n;
  62. {
  63.     char far *p;
  64.     static int counter;
  65.  
  66.     p = MK_FP(0xb800, 0x10 + n);        /* p points in screen buffer */
  67.     *p = counter;
  68.     *(p + 1) = counter;
  69.     *(p + 2) = counter;
  70.     *(p + 3) = counter;
  71.     ++counter;
  72. }
  73. #endif
  74.  
  75.     void
  76. vim_delay()
  77. {
  78.     delay(500);
  79. }
  80.  
  81. /*
  82.  * this version of remove is not scared by a readonly (backup) file
  83.  */
  84.     int
  85. remove(name)
  86.     char *name;
  87. {
  88.     setperm(name, 0);    /* default permissions */
  89.     return unlink(name);
  90. }
  91.  
  92. /*
  93.  * flushbuf(): flush the output buffer
  94.  */
  95.     void
  96. flushbuf()
  97. {
  98.     if (bpos != 0)
  99.     {
  100.         write(0, (char *)outbuf, (long)bpos);
  101.         bpos = 0;
  102.     }
  103. }
  104.  
  105. /*
  106.  * outchar(c): put a character into the output buffer.
  107.  * Flush it if it becomes full.
  108.  */
  109.     void
  110. outchar(c)
  111.     unsigned c;
  112. {
  113.     outbuf[bpos] = c;
  114.     ++bpos;
  115.     if (bpos >= BSIZE)
  116.         flushbuf();
  117. }
  118.  
  119. /*
  120.  * outstr(s): put a string character at a time into the output buffer.
  121.  */
  122.     void
  123. outstr(s)
  124.     register char *s;
  125. {
  126.     if (!s)            /* s is NULL in case of not defined termcap entry */
  127.         return;
  128.     /*
  129.      * The prefix ESC| is used to emulate capabilities
  130.      * missing in ansi.sys by direct calls to conio routines.
  131.      * If we want to avoid this we need the nansi.sys driver. (jw)
  132.      * Only works if the string starts with ESC!
  133.      */
  134.     if (s[0] == ESC && s[1] == '|')
  135.     {
  136.         flushbuf();
  137.         switch (s[2])
  138.         {
  139.         case 'L':    insline();
  140.                          return;
  141.  
  142.         case 'M':    delline();
  143.                     return;
  144.  
  145.         default:    outstr("OOPS");
  146.                     return;
  147.         }
  148.     }
  149. #ifdef TERMCAP
  150.     tputs(s, 1, outchar);
  151. #else
  152.     while (*s)
  153.         outchar(*s++);
  154. #endif
  155. }
  156.  
  157. #define POLL_SPEED 10    /* milliseconds between polls */
  158.  
  159. /*
  160.  * simulate WaitForChar() by slowly polling with bioskey(1).
  161.  * this even works through the serial line after a 'ctty com1' (jw).
  162.  * kbhit() is not used, because then CTRL-C will be catched by DOS (mool).
  163.  */
  164.  
  165.     static int
  166. WaitForChar(msec)
  167.     int msec;
  168. {
  169.     do
  170.     {
  171.         if (bioskey(1) || cbrk_pressed)
  172.             return 1;
  173.         delay(POLL_SPEED);
  174.         msec -= POLL_SPEED;
  175.     }
  176.     while (msec >= 0);
  177.     return 0;
  178. }
  179.  
  180. /*
  181.  * GetChars(): low level input function.
  182.  * Get characters from the keyboard.
  183.  * If type == T_PEEK do not wait for characters.
  184.  * If type == T_WAIT wait a short time for characters.
  185.  * If type == T_BLOCK wait for characters.
  186.  */
  187.     int
  188. GetChars(buf, maxlen, type)
  189.     char        *buf;
  190.     int         maxlen;
  191.     int         type;
  192. {
  193.     int         len = 0;
  194.     int         time = 1000;    /* one second */
  195.     int         i;
  196.     int            c;
  197.  
  198. /*
  199.  * if we got a ctrl-C when we were busy, there will be a "^C" somewhere
  200.  * on the sceen, so we need to redisplay it.
  201.  */
  202.     if (delayed_redraw)
  203.     {
  204.         delayed_redraw = FALSE;
  205.         updateScreen(CLEAR);
  206.         setcursor();
  207.         flushbuf();
  208.     }
  209.  
  210.     switch (type)
  211.     {
  212.     case T_PEEK:
  213.         time = 1;
  214.          /* FALLTHROUGH */
  215.  
  216.     case T_WAIT:
  217.         if (WaitForChar(time) == 0)     /* no character available */
  218.             return 0;
  219.         break;
  220.  
  221.     case T_BLOCK:
  222.     /*
  223.      * If there is no character available within 2 seconds (default)
  224.      * write the autoscript file to disk
  225.      */
  226.         if (WaitForChar((int)p_ut) == 0)
  227.             updatescript(0);
  228.     }
  229.  
  230. /*
  231.  * try to read as many characters as there are
  232.  * works for the controlling tty only.
  233.  */
  234.     --maxlen;        /* may get two chars at once */
  235.     /*
  236.      * we will get at least one key. Get more if they are available
  237.      * After a ctrl-break we have to read a 0 (!) from the buffer.
  238.      * bioskey(1) will return 0 if no key is available and when a
  239.      * ctrl-break was typed. When ctrl-break is hit, this does not always
  240.      * implies a key hit.
  241.      */
  242.     cbrk_pressed = FALSE;
  243.     while ((len == 0 || bioskey(1)) && len < maxlen)
  244.     {
  245.         c = bioskey(0);            /* get the key */
  246.         if (c == 0)                /* ctrl-break */
  247.             c = 3;                /* return a CTRL-C */
  248.         if ((c & 0xff) == 0)
  249.         {
  250.             if (c == 0x0300)        /* CTRL-@ is 0x0300, translated into K_ZERO */
  251.                 c = K_ZERO;
  252.             else        /* extended key code 0xnn00 translated into K_NUl, nn */
  253.             {
  254.                 c >>= 8;
  255.                 *buf++ = K_NUL;
  256.                 ++len;
  257.             }
  258.         }
  259.  
  260.         *buf++ = c;
  261.         len++;
  262.     }
  263.     return len;
  264. }
  265.  
  266. extern int _fmode;
  267.  
  268.     void
  269. textfile(on)
  270.     int on;
  271. {
  272.     /*
  273.      * in O_TEXT mode we were surprised by reading a shorter file,
  274.      * as the ^M characters magically disappear in the read() system call.
  275.      * grrr.
  276.      */
  277.     _fmode = on ? O_TEXT : O_BINARY;
  278. }
  279.  
  280. /*
  281.  * We have no job control, fake it by starting a new shell.
  282.  */
  283.     void
  284. mch_suspend()
  285. {
  286.     outstr("new shell started\n");
  287.     call_shell(NULL, 0);
  288. }
  289.  
  290. /*
  291.  * we do not use windows, there is not much to do here
  292.  */
  293.     void
  294. mch_windinit()
  295. {
  296.     textfile(p_tx);
  297.     flushbuf();
  298. }
  299.  
  300.     void
  301. check_win(argc, argv)
  302.     int        argc;
  303.     char    **argv;
  304. {
  305.     if (!isatty(0) || !isatty(1))
  306.     {
  307.         fprintf(stderr, "VIM: no controlling terminal\n");
  308.         exit(2);
  309.     }
  310. }
  311.  
  312. /*
  313.  * fname_case(): Set the case of the filename, if it already exists.
  314.  *                 msdos filesystem is far to primitive for that. do nothing.
  315.  */
  316.     void
  317. fname_case(name)
  318.     char *name;
  319. {
  320. }
  321.  
  322. /*
  323.  * settitle(): set titlebar of our window.
  324.  * Dos console has no title.
  325.  */
  326.     void
  327. settitle(str)
  328.     char *str;
  329. {
  330. }
  331.  
  332.     void
  333. resettitle()
  334. {
  335. }
  336.  
  337. /*
  338.  * get name of current directory into buffer 'buf' of length 'len' bytes
  339.  */
  340.     int
  341. dirname(buf, len)
  342.     char    *buf;
  343.     int        len;
  344. {
  345.     return (int)getcwd(buf, len);
  346. }
  347.  
  348. /*
  349.  * get absolute filename into buffer 'buf' of length 'len' bytes
  350.  */
  351.     int
  352. FullName(fname, buf, len)
  353.     char    *fname, *buf;
  354.     int        len;
  355. {
  356.     /* failed, because we are under MSDOS */
  357.     strncpy(buf, fname, len);
  358.     return 0;
  359. }
  360.  
  361. /*
  362.  * get file permissions for 'name'
  363.  * -1 : error
  364.  * else FA_attributes defined in dos.h
  365.  */
  366.     long
  367. getperm(name)
  368.     char *name;
  369. {
  370.     int r;
  371.  
  372.     r = _chmod(name, 0, 0);         /* get file mode */
  373.     return r;
  374. }
  375.  
  376. /*
  377.  * set file permission for 'name' to 'perm'
  378.  */
  379.     int
  380. setperm(name, perm)
  381.     char    *name;
  382.     int        perm;
  383. {
  384.     perm &= ~FA_ARCH;
  385.     return _chmod(name, 1, perm);
  386. }
  387.  
  388. /*
  389.  * check if "name" is a directory
  390.  */
  391.     int
  392. isdir(name)
  393.     char *name;
  394. {
  395.     return (_chmod(name, 0, 0) & FA_DIREC) ? 1 : 0;
  396. }
  397.  
  398. /*
  399.  * Careful: mch_windexit() may be called before mch_windinit()!
  400.  */
  401.     void
  402. mch_windexit(r)
  403.     int r;
  404. {
  405.     flushbuf();
  406.  
  407.     settmode(0);
  408.     stopscript();                 /* remove autoscript file */
  409.     exit(r);
  410. }
  411.  
  412. /*
  413.  * function for ctrl-break interrupt
  414.  */
  415.     void interrupt
  416. catch_cbrk()
  417. {
  418.     cbrk_pressed = TRUE;
  419.     ctrlc_pressed = TRUE;
  420. }
  421.  
  422. /*
  423.  * ctrl-break handler for DOS. Never called when a ctrl-break is typed, because
  424.  * we catch interrupt 1b. If you type ctrl-C while Vim is waiting for a
  425.  * character this function is not called. When a ctrl-C is typed while Vim is
  426.  * busy this function may be called. By that time a ^C has been displayed on
  427.  * the screen, so we have to redisplay the screen. We can't do that here,
  428.  * because we may be called by DOS. The redraw is in GetChars().
  429.  */
  430.     static int
  431. cbrk_handler()
  432. {
  433.     delayed_redraw = TRUE;
  434.     return 1;                 /* resume operation after ctrl-break */
  435. }
  436.  
  437. /*
  438.  * function for critical error interrupt
  439.  * For DOS 1 and 2 return 0 (Ignore).
  440.  * For DOS 3 and later return 3 (Fail)
  441.  */
  442.     void interrupt
  443. catch_cint(bp, di, si, ds, es, dx, cx, bx, ax)
  444.     unsigned bp, di, si, ds, es, dx, cx, bx, ax;
  445. {
  446.     ax = (ax & 0xff00);        /* set AL to 0 */
  447.     if (_osmajor >= 3)
  448.         ax |= 3;            /* set AL to 3 */
  449. }
  450.  
  451. /*
  452.  * set the tty in (raw) ? "raw" : "cooked" mode
  453.  *
  454.  * Does not change the tty, as bioskey() works raw all the time.
  455.  */
  456.  
  457. extern void interrupt CINT_FUNC();
  458.  
  459.     void
  460. mch_settmode(raw)
  461.     int  raw;
  462. {
  463.      static int saved_cbrk;
  464.      static void interrupt (*old_cint)();
  465.      static void interrupt (*old_cbrk)();
  466.  
  467.      if (raw)
  468.      {
  469.          saved_cbrk = getcbrk();            /* save old ctrl-break setting */
  470.          setcbrk(0);                        /* do not check for ctrl-break */
  471.          old_cint = getvect(0x24);         /* save old critical error interrupt */
  472.          setvect(0x24, catch_cint);        /* install our critical error interrupt */
  473.          old_cbrk = getvect(0x1B);         /* save old ctrl-break interrupt */
  474.          setvect(0x1B, catch_cbrk);        /* install our ctrl-break interrupt */
  475.          ctrlbrk(cbrk_handler);            /* vim's ctrl-break handler */
  476.      }
  477.      else
  478.      {
  479.          setcbrk(saved_cbrk);            /* restore ctrl-break setting */
  480.          setvect(0x24, old_cint);        /* restore critical error interrupt */
  481.          setvect(0x1B, old_cbrk);        /* restore ctrl-break interrupt */
  482.          /* restore ctrl-break handler, how ??? */
  483.      }
  484. }
  485.  
  486.     int
  487. mch_get_winsize()
  488. {
  489.     struct text_info ti;
  490.  
  491.     debug("mch_get_winsize\n");
  492.     if (!term_console)
  493.         return 1;
  494.     gettextinfo(&ti);
  495.     Columns = ti.screenwidth;
  496.     Rows = ti.screenheight;
  497.     if (Columns < 5 || Columns > MAX_COLUMNS ||
  498.                     Rows < 2 || Rows > MAX_COLUMNS)
  499.     {
  500.         /* these values are not used. overwritten by termcap size or default */
  501.         Columns = 80;
  502.         Rows = 24;
  503.         return 1;
  504.     }
  505.     return 0;
  506. }
  507.  
  508.     void
  509. mch_set_winsize()
  510. {
  511.     /* should try to set the window size to Rows and Columns */
  512.     /* may involve switching display mode.... */
  513. }
  514.  
  515.     int
  516. call_shell(cmd, filter)
  517.     char    *cmd;
  518.     int     filter;         /* if != 0: called by dofilter() */
  519. {
  520.     int        x;
  521.     char    newcmd[200];
  522.  
  523.     flushbuf();
  524.  
  525.     settmode(0);        /* set to cooked mode */
  526.  
  527.     if (cmd == NULL)
  528.         x = system(p_sh);
  529.     else
  530.     {                     /* we use "command" to start the shell, slow but easy */
  531.         sprintf(newcmd, "%s /c %s", p_sh, cmd);
  532.         x = system(newcmd);
  533.     }
  534.     outchar('\n');
  535.     settmode(1);        /* set to raw mode */
  536.  
  537.     if (x)
  538.     {
  539.         smsg("%d returned", x);
  540.         outchar('\n');
  541.     }
  542.  
  543.     resettitle();
  544.     return x;
  545. }
  546.  
  547. /*
  548.  * check for an "interrupt signal": CTRL-break or CTRL-C
  549.  */
  550.     void
  551. breakcheck()
  552. {
  553.     if (ctrlc_pressed)
  554.     {
  555.         ctrlc_pressed = FALSE;
  556.         got_int = TRUE;
  557.         flush_buffers();        /* remove all typeahead and macro stuff */
  558.     }
  559. }
  560.  
  561. /*
  562.  * add extention to filename - change path/fo.o to path/fo_o.ext
  563.  *
  564.  * The general case is in script.c, but MS-DOS has a restricted namespace.
  565.  * Assumed that fname is a valid name found in the filesystem we assure that
  566.  * the return value is a different name and ends in ".ext".
  567.  */
  568.  
  569.     char *
  570. modname(fname, ext)
  571.     char *fname, *ext;
  572. {
  573.     char            *retval;
  574.     register char   *s;
  575.     register char   *ptr;
  576.     register int    fnamelen, extlen;
  577.     char            currentdir[512];
  578.  
  579.     extlen = strlen(ext);
  580.  
  581.     /*
  582.      * if there is no filename we must get the name of the current directory
  583.      * (we need the full path in case :cd is used)
  584.      */
  585.     if (fname == NULL || *fname == NUL)
  586.     {
  587.         (void)dirname(currentdir, 511);
  588.         strcat(currentdir, "\\");
  589.         fnamelen = strlen(currentdir);
  590.     }
  591.     else
  592.         fnamelen = strlen(fname);
  593.     retval = alloc((unsigned) (fnamelen + extlen + 1));
  594.     if (retval != NULL)
  595.     {
  596.         if (fname == NULL || *fname == NUL)
  597.             strcpy(retval, currentdir);
  598.         else
  599.             strcpy(retval, fname);
  600.         /*
  601.          * search backwards until we hit a '\' or ':' replacing all '.' by '_'
  602.          * Then truncate what is after the '\' or ':' to 8 characters and append
  603.          * the 3 letter ext seperated with a '.'.
  604.          */
  605.         for (ptr = retval + fnamelen; ptr >= retval; ptr--)
  606.         {
  607.             if (*ptr == '.')
  608.                 *ptr = '_';
  609.             if (*ptr == '\\' || *ptr == ':')
  610.                 break;
  611.         }
  612.         ptr++;
  613.         /* the filename has at most 8 characters. */
  614.         if (strlen(ptr) > 8)
  615.             ptr[8] = '\0';
  616.         s = ptr + strlen(ptr);
  617.         /* ext must start with '.' and cannot exceed 3 more characters. */
  618.         strncpy(s, ext, 4);
  619.         s[4] = '\0';
  620.         *s = '.';
  621.         if (fname != NULL && strcmp(fname, retval) == 0)
  622.         {
  623.             /* after modification still the same name? */
  624.             /* if basename has less then 8 characters append '_' */
  625.             if (s < ptr + 8)
  626.             {
  627.                 *s++ = '_';
  628.                 strncpy(s, ext, 4);
  629.                 s[4] = '\0';
  630.                 *s = '.';
  631.             }
  632.             else
  633.             {
  634.                 /* we must search for a character that can be replaced by '_' */
  635.                 while (--s >= ptr)
  636.                 {
  637.                     if (*s != '_')
  638.                     {
  639.                         *s = '_';
  640.                         break;
  641.                     }
  642.                 }
  643.             }
  644.             if (s < ptr)
  645.             {
  646.                 /* fname was "________.<ext>" how tricky! */
  647.                 *ptr = 'v';
  648.             }
  649.         }
  650.     }
  651.     return retval;
  652. }
  653.  
  654. #ifdef WILD_CARDS
  655. #define FL_CHUNK 32
  656.  
  657.     static void
  658. addfile(fl, f)
  659.     FileList    *fl;
  660.     char        *f;
  661. {
  662.     if (!fl->file)
  663.     {
  664.         fl->file = (char **)alloc(sizeof(char *) * FL_CHUNK);
  665.         if (!fl->file)
  666.             return;
  667.         fl->nfiles = 0;
  668.         fl->maxfiles = FL_CHUNK;
  669.     }
  670.     if (fl->nfiles >= fl->maxfiles)
  671.     {
  672.         char    **t;
  673.         int        i;
  674.  
  675.         t = (char **)alloc(sizeof(char *) * (fl->maxfiles + FL_CHUNK));
  676.         if (!t)
  677.             return;
  678.         for (i = fl->nfiles - 1; i >= 0; i--)
  679.             t[i] = fl->file[i];
  680.         free(fl->file);
  681.         fl->file = t;
  682.         fl->maxfiles += FL_CHUNK;
  683.     }
  684.     fl->file[fl->nfiles++] = f;
  685. }
  686.  
  687.     static int
  688. pstrcmp(a, b)
  689.     char **a, **b;
  690. {
  691.     return (strcmp(*a, *b));
  692. }
  693.  
  694.     static int
  695. no_wildcard(s)
  696.     char *s;
  697. {
  698.     if (s)
  699.     {
  700.         while (*s)
  701.         {
  702.             if (*s == '?' || *s == '*')
  703.                 return 0;
  704.             s++;
  705.         }
  706.     }
  707.     return 1;
  708. }
  709.  
  710.     static void
  711. strlowcpy(d, s)
  712.     char *d, *s;
  713. {
  714.     while (*s)
  715.         *d++ = tolower(*s++);
  716.     *d = '\0';
  717. }
  718.  
  719.     static int
  720. expandpath(fl, path, fonly, donly, notf)
  721.     FileList    *fl;
  722.     char        *path;
  723.     int            fonly, donly, notf;
  724. {
  725.     char    buf[MAXPATH];
  726.     char    *p, *s, *e;
  727.     int        lastn, c, r;
  728.     struct    ffblk fb;
  729.  
  730.     lastn = fl->nfiles;
  731.  
  732. /*
  733.  * Find the first part in the path name that contains a wildcard.
  734.  * Copy it into buf, including the preceding characters.
  735.  */
  736.     p = buf;
  737.     s = NULL;
  738.     e = NULL;
  739.     while (*path)
  740.     {
  741.         if (*path == '\\' || *path == ':' || *path == '/')
  742.         {
  743.             if (e)
  744.                 break;
  745.             else
  746.                 s = p;
  747.         }
  748.         if (*path == '*' || *path == '?')
  749.             e = p;
  750.         *p++ = *path++;
  751.     }
  752.     e = p;
  753.     if (s)
  754.         s++;
  755.     else
  756.         s = buf;
  757.  
  758.     /* now we have one wildcard component between s and e */
  759.     *e = '\0';
  760.     r = 0;
  761.     if ((c = findfirst(buf, &fb, *path ? FA_DIREC : 0)) != 0)
  762.     {
  763.         /* not found */
  764.         strcpy(e, path);
  765.         if (notf)
  766.             addfile(fl, strsave(buf));
  767.         return 1; /* unexpanded or empty */
  768.     }
  769.     while (!c)
  770.     {
  771.         strlowcpy(s, fb.ff_name);
  772.         if (*s != '.' || (s[1] != '\0' && (s[1] != '.' || s[2] != '\0')))
  773.         {
  774.             strcat(buf, path);
  775.             if (no_wildcard(path))
  776.                 addfile(fl, strsave(buf));
  777.             else
  778.                 r |= expandpath(fl, buf, fonly, donly, notf);
  779.         }
  780.         c = findnext(&fb);
  781.     }
  782.     qsort(fl->file + lastn, fl->nfiles - lastn, sizeof(char *), pstrcmp);
  783.     return r;
  784. }
  785.  
  786. /*
  787.  * MSDOS rebuilt of Scott Ballantynes ExpandWildCard for amiga/arp.
  788.  * jw
  789.  */
  790.  
  791.     int
  792. ExpandWildCards(num_pat, pat, num_file, file, files_only, list_notfound)
  793.     int     num_pat;
  794.     char    **pat;
  795.     int     *num_file;
  796.     char    ***file;
  797.     int     files_only, list_notfound;
  798. {
  799.     int            i, r = 0;
  800.     FileList    f;
  801.  
  802.     f.file = NULL;
  803.     f.nfiles = 0;
  804.     for (i = 0; i < num_pat; i++)
  805.     {
  806.         if (no_wildcard(pat[i]))
  807.             addfile(&f, strsave(pat[i]));
  808.         else
  809.             expandpath(&f, pat[i], files_only, 0, list_notfound);
  810.     }
  811.     *num_file = f.nfiles;
  812.     *file = f.file;
  813.     return r;
  814. }
  815.  
  816.     void
  817. FreeWild(num, file)
  818.     int        num;
  819.     char    **file;
  820. {
  821.     if (file == NULL || num <= 0)
  822.         return;
  823.     while (num--)
  824.         free(file[num]);
  825.     free(file);
  826. }
  827. #endif /* WILD_CARDS */
  828.